use axum_app::{create_axum_app, database::DatabaseManager, iroh_wrapper::IrohNodeWrapper};
use rig_app::{AgentManager, ToolManager};
use rusqlite::ffi::{sqlite3, sqlite3_api_routines, sqlite3_auto_extension};
use sqlite_vec::sqlite3_vec_init;
use std::sync::Arc;
use tokio::signal;
use tower_http::cors::{Any, CorsLayer};

type SqliteExtensionFn =
    unsafe extern "C" fn(*mut sqlite3, *mut *mut i8, *const sqlite3_api_routines) -> i32;

#[tokio::main]
async fn main() {
    // Initialize tracing

    // Initialize the `sqlite-vec`extension
    // See: https://alexgarcia.xyz/sqlite-vec/rust.html
    unsafe {
        sqlite3_auto_extension(Some(std::mem::transmute::<*const (), SqliteExtensionFn>(
            sqlite3_vec_init as *const (),
        )));
    }

    // Create AgentManager and ToolManager instances
    let agent_manager =
        Arc::new(AgentManager::new("agents.json").expect("Failed to create AgentManager"));
    let tool_manager =
        Arc::new(ToolManager::new("mcp.json").expect("Failed to create ToolManager"));

    // Create DatabaseManager instance
    let database =
        Arc::new(DatabaseManager::new("app.db").expect("Failed to create DatabaseManager"));

    // Create IrohNodeWrapper instance
    let iroh_node = Arc::new(IrohNodeWrapper::default());

    // Start Iroh node in background
    println!("Attempting to open Iroh node...");
    let iroh_node_clone = iroh_node.clone();
    tokio::spawn(async move {
        match iroh_node_clone.open_node().await {
            Ok(_) => println!("Iroh node opened successfully."),
            Err(e) => eprintln!("Failed to open Iroh node: {}", e),
        }
    });

    let cors = CorsLayer::new()
        .allow_origin(Any)
        .allow_methods(Any)
        .allow_headers(Any);

    // Build our application with the router
    let app = create_axum_app(agent_manager, tool_manager, iroh_node, database).layer(cors);

    // Run our application
    println!("Starting Axum server on 0.0.0.0:1421...");
    let listener = tokio::net::TcpListener::bind("0.0.0.0:1421").await.unwrap();
    println!("Server listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await
        .unwrap();
}

async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install SIGTERM handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }

    println!("signal received, starting graceful shutdown");
    // Close the Iroh node gracefully
    // Note: We can't access iroh_node here, so we'll create a temporary instance
    IrohNodeWrapper::default().close_node().await;
    println!("Iroh node closed during shutdown.");
}
